import * as vscode from 'vscode';
import * as path from 'path';
import { disposeAll, getNonce } from './util';

/**
 * Define the document (the data model) used for mesh files.
 */
export class MDXDocument implements vscode.CustomDocument {

    private isDisposed = false;
    private disposables: vscode.Disposable[] = [];

    public constructor(public uri: vscode.Uri) { }

    private readonly _onDidDispose = this._register(new vscode.EventEmitter<void>());
    /**
     * Fired when the document is disposed of.
     */
    public readonly onDidDispose = this._onDidDispose.event;

    private readonly _onDidChangeDocument = this._register(new vscode.EventEmitter<void>());
    /**
     * Fired to notify webviews that the document has changed.
     */
    public readonly onDidChangeContent = this._onDidChangeDocument.event;

    /**
     * Called by VS Code when there are no more references to the document.
     * 
     * This happens when all editors for it have been closed.
     */
    dispose(): void {
        this._onDidDispose.fire();
        if (this.isDisposed) {
            return;
        }
        this.isDisposed = true;
        disposeAll(this.disposables);
    }

    private _register<T extends vscode.Disposable>(value: T): T {
        if (this.isDisposed) {
            value.dispose();
        } else {
            this.disposables.push(value);
        }
        return value;
    }
}

export class MDXEditorProvider implements vscode.CustomReadonlyEditorProvider<MDXDocument>{

    public static register(context: vscode.ExtensionContext): vscode.Disposable {
        const provider = new MDXEditorProvider(context);
        const providerRegistration = vscode.window.registerCustomEditorProvider(MDXEditorProvider.viewType, provider);
        return providerRegistration;
    }

    private static readonly viewType = 'wc3.mdx';

    constructor(
        private readonly _context: vscode.ExtensionContext
    ) { }


    openCustomDocument(
        uri: vscode.Uri,
        openContext: vscode.CustomDocumentOpenContext,
        token: vscode.CancellationToken): MDXDocument | Thenable<MDXDocument> {

        const document = new MDXDocument(uri);
        const listeners: vscode.Disposable[] = [];

        document.onDidDispose(() => disposeAll(listeners));

        return document;

    }
    resolveCustomEditor(
        document: MDXDocument,
        webviewPanel: vscode.WebviewPanel,
        token: vscode.CancellationToken): void | Thenable<void> {


        // Setup initial content for the webview
        webviewPanel.webview.options = {
            enableScripts: true,
        };
        webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview, document);

        // Wait for the webview to be properly ready before we init
        webviewPanel.webview.onDidReceiveMessage(e => {
            if (e.type === 'ready') {
                this.postMessage(webviewPanel, 'init', {});
            }
        });
    }


    private getMediaPath(scheme: string, mediaFile: string): vscode.Uri {
        return vscode.Uri.file(path.join(this._context.extensionPath, 'media', mediaFile))
            .with({ scheme: scheme });
    }

    private getSettings(uri: vscode.Uri): string {
        const initialData = {
            fileToLoad: uri.toString(),
            currentDir: path.dirname(uri.toString())
        }
        return `<meta id="vscode-blpviewer-data" data-settings="${JSON.stringify(initialData).replace(/"/g, '&quot;')}">`
    }

    private getScripts(scheme: string, nonce: string): string {
        const scripts = [
            this.getMediaPath(scheme, 'war3-model.browser.js'),
            this.getMediaPath(scheme, 'mdx/main.js'),
        ];
        return scripts
            .map(source => `<script nonce="${nonce}" src="${source}"></script>`)
            .join('\n');
    }

    /**
     * Get the static html used for the editor webviews.
     */
    private getHtmlForWebview(webview: vscode.Webview, document: MDXDocument): string {
        const fileToLoad = document.uri.scheme === "file" ?
            webview.asWebviewUri(vscode.Uri.file(document.uri.fsPath)) :
            document.uri;

        const styleUri = webview.asWebviewUri(vscode.Uri.file(
            path.join(this._context.extensionPath, 'media', 'mdx/preview.css')
        ));

        const mediaUri = webview.asWebviewUri(vscode.Uri.file(
            path.join(this._context.extensionPath, 'media')
        ));

        // Use a nonce to whitelist which scripts can be run
        const nonce = getNonce();

        return /* html */`
			<!DOCTYPE html>
			<html lang="en">
			<head>
				<meta charset="UTF-8">

				<!--
				Use a content security policy to only allow loading images from https or from our extension directory,
				and only allow scripts that have a specific nonce.
				-->

				<meta http-equiv="Content-Security-Policy" content="default-src ${webview.cspSource} 'self' 'unsafe-eval' blob: data:; img-src ${webview.cspSource} 'self' 'unsafe-eval' blob: data:; style-src ${webview.cspSource} 'unsafe-inline'; script-src ${webview.cspSource} 'self' 'unsafe-eval' blob: data:;">

				<meta name="viewport" content="width=device-width, initial-scale=1.0">
				<link href="${styleUri}" rel="stylesheet" />
				<base href="${mediaUri}/">
				${this.getSettings(fileToLoad)}
				<title>MDX预览</title>
				
			</head>
			<body>
                <div class="container" dropzone="copy">
                    <div class="inner">
                        <canvas id="canvas" width="300" height="300"></canvas>
                    </div>
                    <div class="controls">
                        <label>队伍颜色<input type="color" id="color" value="#ff0000"></label>
                        <label>动作<select id="select"><option>None</option></select></label>
                        <label>Target z <input type="range" id="targetZ" value="50" min="0" max="200"></label>
                        <label>镜头<input type="range" id="distance" value="500" min="100" max="1000"></label>
                        <label>线框<input type="checkbox" id="wireframe"></label>
                        <fieldset class="skeleton-group">
                            <label>骨骼<input type="checkbox" id="show_skeleton"></label>
                            <label>节点<select id="skeleton" disabled><option value="*">All</option></select></label>
                        </fieldset>
                    </div>
                </div>
			</body>
				${this.getScripts('vscode-resource', nonce)}
			</html>`;
    }


    private readonly _callbacks = new Map<number, (response: any) => void>();

    private postMessage(panel: vscode.WebviewPanel, type: string, body: any): void {
        panel.webview.postMessage({ type, body });
    }

    private onMessage(document: MDXDocument, message: any) {
        switch (message.type) {
            case 'response':
                const callback = this._callbacks.get(message.requestId);
                callback?.(message.body);
                return;
        }
    }

}